package com.senyint.sys.utils.quartz;

import java.text.ParseException;
import java.util.Date;

import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.senyint.sys.utils.StringUtils;

/**
 * 规则定时器任务 {@code QuartzJobCornUtil}类
 * <p>
 * 
 * @author NaiFei Wang, wangnaifei@senyint.com
 * @date 2016年3月31日 上午10:37:25
 * @version 1.0
 * @since jdk1.7+
 */
// "0 0 12 * * ?" 每天中午12点触发
// "0 15 10 ? * *" 每天上午10:15触发
// "0 15 10 * * ?" 每天上午10:15触发
// "0 15 10 * * ? *" 每天上午10:15触发
// "0 15 10 * * ? 2005" 2005年的每天上午10:15触发
// "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
// "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
// "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
// "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
// "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
// "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
// "0 15 10 15 * ?" 每月15日上午10:15触发
// "0 15 10 L * ?" 每月最后一日的上午10:15触发
// "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
// "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
// "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
// "0 6 * * *" 每天早上6点
// "0 */2 * * *" 每两个小时
// "0 23-7/2，8 * * *" 晚上11点到早上8点之间每两个小时，早上八点
// "0 11 4 * 1-3" 每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点
// "0 4 1 1 *" 1月1日早上4点
public class QuartzJobCornUtil extends QuartzJobUtils {

	private static final Logger log = LoggerFactory.getLogger(QuartzJobCornUtil.class);

	/**
	 * merge 一个job
	 * <p>
	 * 
	 * @author NaiFei Wang, wangnaifei@senyint.com
	 * @date 2016年3月30日 下午9:01:43
	 * @version 1.0
	 * @param scheduler
	 * @param triggerId
	 * @param triggerGroup
	 * @param jobId
	 * @param jobGroup
	 * @param jobClass
	 * @param jobDataMap
	 * @param cronExpression
	 * @throws SchedulerException
	 */
	public static void mergeJob(Scheduler scheduler, String triggerId, String triggerGroup, String jobId, String jobGroup, Class<? extends Job> jobClass, JobDataMap jobDataMap, String cronExpression) throws SchedulerException {
		log.debug("triggerId:{}", triggerId);
		log.debug("triggerGroup:{}", triggerGroup);
		TriggerKey triggerKey = TriggerKey.triggerKey(triggerId, triggerGroup);
		log.debug("triggerKey:{}", triggerKey);
		Trigger trigger = scheduler.getTrigger(triggerKey);
		if (null == trigger) {
			scheduleJob(scheduler, jobId, jobGroup, jobClass, triggerKey, jobDataMap, cronExpression);
		} else {
			rescheduleJob(scheduler, (CronTrigger) trigger, jobDataMap, cronExpression);
		}
	}

	/**
	 * 新增一个job
	 * <p>
	 * 
	 * @author NaiFei Wang, wangnaifei@senyint.com
	 * @date 2016年3月30日 下午9:04:30
	 * @version 1.0
	 * @param scheduler
	 * @param jobId
	 * @param jobGroup
	 * @param jobClass
	 * @param triggerId
	 * @param triggerGroup
	 * @param jobDataMap
	 * @param cronExpression
	 * @throws SchedulerException
	 */
	public static void scheduleJob(Scheduler scheduler, String jobId, String jobGroup, Class<? extends Job> jobClass, String triggerId, String triggerGroup, JobDataMap jobDataMap, String cronExpression) throws SchedulerException {
		log.debug("triggerId:{}", triggerId);
		log.debug("triggerGroup:{}", triggerGroup);
		TriggerKey triggerKey = TriggerKey.triggerKey(triggerId, triggerGroup);
		scheduleJob(scheduler, jobId, jobGroup, jobClass, triggerKey, jobDataMap, cronExpression);
	}

	/**
	 * 新增一个job
	 * <p>
	 * 
	 * @author NaiFei Wang, wangnaifei@senyint.com
	 * @date 2016年3月30日 下午9:01:59
	 * @version 1.0
	 * @param scheduler
	 * @param jobId
	 * @param jobGroup
	 * @param jobClass
	 * @param triggerKey
	 * @param jobDataMap
	 * @param cronExpression
	 * @throws SchedulerException
	 */
	public static void scheduleJob(Scheduler scheduler, String jobId, String jobGroup, Class<? extends Job> jobClass, TriggerKey triggerKey, JobDataMap jobDataMap, String cronExpression) throws SchedulerException {
		log.debug("jobId:{}", jobId);
		log.debug("jobGroup:{}", jobGroup);
		JobKey jobKey = JobKey.jobKey(jobId, jobGroup);
		scheduleJob(scheduler, jobKey, jobClass, triggerKey, jobDataMap, cronExpression);
	}

	/**
	 * 新增一个job
	 * <p>
	 * 
	 * @author NaiFei Wang, wangnaifei@senyint.com
	 * @date 2016年3月30日 下午9:02:09
	 * @version 1.0
	 * @param scheduler
	 * @param jobKey
	 * @param jobClass
	 * @param triggerKey
	 * @param jobDataMap
	 * @param cronExpression
	 * @throws SchedulerException
	 */
	public static void scheduleJob(Scheduler scheduler, JobKey jobKey, Class<? extends Job> jobClass, TriggerKey triggerKey, JobDataMap jobDataMap, String cronExpression) throws SchedulerException {
		log.debug("jobKey:{}", jobKey);
		log.debug("jobClass:{}", jobClass);
		JobDetail jobDetail = JobBuilder.newJob(jobClass)//
				.withIdentity(jobKey)//
				.storeDurably(true)// 没有trigger关联时自动删除
				.requestRecovery()// 失败后重做
				.build();
		jobDetail.getJobDataMap().putAll(jobDataMap);
		scheduleJob(scheduler, jobDetail, triggerKey, cronExpression);
	}

	/**
	 * 新增一个job
	 * <p>
	 * 
	 * @author NaiFei Wang, wangnaifei@senyint.com
	 * @date 2016年3月30日 下午9:02:23
	 * @version 1.0
	 * @param scheduler
	 * @param jobDetail
	 * @param triggerKey
	 * @param cronExpression
	 * @throws SchedulerException
	 */
	public static void scheduleJob(Scheduler scheduler, JobDetail jobDetail, TriggerKey triggerKey, String cronExpression) throws SchedulerException {
		log.debug("jobDetail:{}", jobDetail);
		log.debug("triggerKey:{}", triggerKey);
		log.debug("cronExpression:{}", cronExpression);
		CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build();
		log.debug("trigger:{}", trigger);
		scheduler.scheduleJob(jobDetail, trigger);
	}

	/**
	 * 更新一个trigger
	 * <p>
	 * 
	 * @author NaiFei Wang, wangnaifei@senyint.com
	 * @date 2016年3月30日 下午9:02:38
	 * @version 1.0
	 * @param scheduler
	 * @param trigger
	 * @param jobDataMap
	 * @param cronExpression
	 * @throws SchedulerException
	 */
	public static void rescheduleJob(Scheduler scheduler, CronTrigger trigger, JobDataMap jobDataMap, String cronExpression) throws SchedulerException {
		// Trigger已存在，那么更新相应的定时设置
		CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

		TriggerKey triggerKey = trigger.getKey();

		// 按新的cronExpression表达式重新构建trigger
		trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

		// 重新设置data
		trigger.getJobDataMap().putAll(jobDataMap);

		// 按新的trigger重新设置job执行
		scheduler.rescheduleJob(triggerKey, trigger);
	}

	/**
	 * 验证频率表达式
	 * <p>
	 * 
	 * @author NaiFei Wang, wangnaifei@senyint.com
	 * @date 2016年3月30日 下午4:38:10
	 * @version 1.0
	 * @param str
	 * @return
	 */
	public static boolean isValidExpression(final String str) {
		if (StringUtils.isNotEmpty(str)) {
			try {
				CronExpression cronExpression = new CronExpression(str);
				CronTriggerImpl trigger = new CronTriggerImpl();
				trigger.setCronExpression(cronExpression);
				Date date = trigger.computeFirstFireTime(null);
				return date != null && date.after(new Date());
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		return false;
	}
}
